﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Data.GameData.Utils;
using Data.GameData.Entities;

namespace Data.GameData
{
    public class Population : GameObject
    {
        #region Attributes
        private EPopulation dbPopulation;
        private static Dictionary<Guid, Population> populationList = new Dictionary<Guid, Population>();

        private SystemBody body;
        private Guid ownerId;
        private double size;
        private int indUnits = 0;
        #endregion

        #region Constructors
        public Population(SystemBody body, Guid owner, double initialSize)
        {
            dbPopulation = new EPopulation();

            this.body = body;
            this.ownerId = owner;
            this.size = initialSize;

            MarkDirty();
        }
        
        public Population(Guid id)
        {
            dbPopulation = new EPopulation();

            if (id != Guid.Empty)
            {
                dbPopulation = EPopulation.LoadFromDB(id);
                Load();
            }
        }

        private Population(EPopulation entity)
        {
            this.dbPopulation = entity;
            Load();
        }
        #endregion

        #region Static Properties
        static public List<Population> Populations
        {
            get
            {
                if (populationList == null || populationList.Count == 0) FetchPopulationsForGame();
                return new List<Population>(populationList.Values);
            }
        }

        static public Population GetPopulation(Guid id)
        {
            if (populationList == null || populationList.Count == 0) FetchPopulationsForGame();

            if (populationList.ContainsKey(id))
                return populationList[id];

            return null;
        }

        static public Population[] GetPopulations()
        {
            return Populations.ToArray();
        }
        #endregion

        #region Static Private Methods
        static private void FetchPopulationsForGame()
        {
            if (populationList == null || populationList.Count == 0)
            {
                populationList = new Dictionary<Guid, Population>();

                List<EPopulation> dbPopulationList = EPopulation.GetAllForGame(Game.ActiveGame.Id);
                lock (populationList)
                {
                    // Create the Plan business objects
                    foreach (EPopulation entity in dbPopulationList)
                    {
                        Population gameObj = new Population(entity);
                        populationList.Add(gameObj.Id, gameObj);
                    }
                }
            }
        }
        #endregion

        #region Properties
        public SystemBody SettledBody
        {
            get { return body; }
            internal set { body = value; }
        }

        public Guid OwnerId
        {
            get { return ownerId; }
            set
            {
                if (ownerId == value) return;
                ownerId = value;
                MarkDirty();
            }
        }

        public double Size
        {
            get { return size; }
            set
            {
                if (size == value) return;
                size = value;
                MarkDirty();
            }
        }

        public int IU
        {
            get { return indUnits; }
            set
            {
                if (indUnits == value) return;
                indUnits = value;
                MarkDirty();
            }
        }
        #endregion

        #region BaseObject overrides - Save, Update, etc
        public override void Save()
        {
            // Save links this is dependent on

            // Save this
            base.Save();

            // Save link dependent on this
        }

        public static void SaveAllForRace(Guid id)
        {
            foreach (Population population in populationList.Values.Where(info => info.OwnerId == id))
            {
                population.Save();
            }
        }

        internal override void Update()
        {
            if (MarkedToDelete)
            {
                // Delete dependencies

                DeleteNow();
            }
            else //if(IsNew || IsDirty)
            {
                if (IsNew)
                {
                    if (populationList == null) populationList = new Dictionary<Guid, Population>();
                    populationList.Add(this.Id, this);
                }

                SaveValues();
                dbPopulation.SaveToDB(IsNew);
            }
        }

        internal override void DeleteNow()
        {
            if (dbPopulation.Values == null)
                dbPopulation.Values = new Stitch.Database.DBRow();

            dbPopulation.Values["id"] = Id;

            dbPopulation.Delete();

            //
            populationList.Remove(Id);
            MarkDeleted();
        }

        internal override void SaveValues()
        {
            if (dbPopulation.Values == null)
                dbPopulation.Values = new Stitch.Database.DBRow();

            dbPopulation.Values["id"] = Id;
            dbPopulation.Values["gameId"] = Game.ActiveGame.Id;
            dbPopulation.Values["size"] = (decimal)Size;
            dbPopulation.Values["iu"] = IU;
            dbPopulation.Values["ownerId"] = OwnerId;
            dbPopulation.Values["populationLinkId"] = Guid.Empty;
        }

        internal override void LoadValues()
        {
            if (dbPopulation.Values == null)
                return;

            Id = dbPopulation.Values["id"];
            Size = (double)dbPopulation.Values["size"];
            IU = dbPopulation.Values["iu"];
            OwnerId = dbPopulation.Values["ownerId"];
        }

        internal override void InitialiseObject()
        {
        }
        #endregion

        public int MaxPopulationSize()
        {
            Race owner = Race.GetRace(ownerId);
            if (owner == null)
                return 0;

            int maxSize = 0;

            if (body.Type == SystemBodyType.PLANET)
            {
                SystemBodyCompatability compatibility = body.Compatability(owner.HabIndex, owner.PerferedEnviroment);

                switch (compatibility)
                {
                    case SystemBodyCompatability.BENIGN:
                        maxSize = 3000 + (250 * owner.EconomicLevel);
                        break;
                    case SystemBodyCompatability.HARSH:
                        maxSize = 1500 + (200 * owner.EconomicLevel);
                        break;
                    case SystemBodyCompatability.HOSTILE:
                        maxSize = 750 + (150 * owner.EconomicLevel);
                        break;
                    case SystemBodyCompatability.DESOLATE:
                        maxSize = 180;
                        break;
                    case SystemBodyCompatability.EXTREME:
                        maxSize = 60;
                        break;
                }
            }
            else if (body.Type == SystemBodyType.MOON)
            {
                SystemBody parentBody = SystemBody.SystemBodies.Find(pBody => pBody.Id == body.Orbits.Id) as SystemBody;
                switch (parentBody.Enviroment)
                {
                    case SystemBodyEnviroment.TYPE_I:
                    case SystemBodyEnviroment.TYPE_O1:
                        maxSize = 20;
                        break;
                    default:
                        maxSize = 60;
                        break;
                }
            }
            else // Asteroid
            {
                maxSize = body.RadiusLM * 20;
            }

            return maxSize;
        }

        public double MonthlyGrowth()
        {
            size = Math.Round(size * 0.01, 1, MidpointRounding.ToEven);
            return Math.Min( size, MaxPopulationSize());
        }

        public double MonthlyIncome()
        {
            int usableIUs = Math.Min(indUnits, (int)Math.Round((size / 2) - 0.5));
            return ((size * body.REI) + usableIUs); //* (sizeModifier) * (1.1 * body.AstBelts)
        }
    }
}
